package org.copyfelix.io;

import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.FileCopyUtils;

public class CopycatFile implements CopycatStream, InitializingBean {

	private Logger log = Logger.getLogger(CopycatFile.class);

	private Map<Integer, Object> options = new HashMap<Integer, Object>();

	private static final String DEFAULT_TEMP_FILE_SUFFIX = ".writing";

	private static final int FILENAME_OPTIONS = 2001;

	private static final int FILE_OPTIONS = 2002;

	private static final Integer RELATIVE_PATH_OPTIONS = 2003;

	private FilenamePattern fileNamePattern;

	private String tempFileSuffix = DEFAULT_TEMP_FILE_SUFFIX;

	private String[] fileFilters;

	private String fileFilter;

	private String[] paths;

	private String path;

	private String targetPath;

	private List<String> allPath = new ArrayList<String>();

	private FileFilter ff;

	public int read(CopycatStream target) throws Exception {
		log.info("CopycatFile.read() BEGIN");
		for (Iterator<String> iterator = allPath.iterator(); iterator.hasNext();) {
			String s = iterator.next();
			File f = new File(s);
			if (!f.isDirectory()) {
				log.error(s + " is not a directory... skipping");
				continue;
			}
			scrollDirectory(target, f);
		}
		log.info("CopycatFile.read() END");
		return 0;
	}

	private void scrollDirectory(CopycatStream target, File f)
			throws IOException, Exception {
		File[] list = f.listFiles(ff);
		for (int i = 0; i < list.length; i++) {
			File file = list[i];
			if (file.isDirectory()) {
				scrollDirectory(target, file);
			} else {
				Map<Integer, Object> options = new HashMap<Integer, Object>();
				options.put(INPUTSTREAM_OPTIONS,
						FileCopyUtils.copyToByteArray(file));
				options.put(FILENAME_OPTIONS, file.getName());
				options.put(FILE_OPTIONS, file);
				options.put(RELATIVE_PATH_OPTIONS, getRelativePath(file));
				target.streamOptions(options);
				target.write();
			}
		}
	}

	/**
	 * restituisce il path relativo, a partire quindi dal valore di paths o path
	 * 
	 * @param file
	 * @return
	 */
	private String getRelativePath(File file) {
		String filePath = file.getParent();
		for (Iterator iterator = allPath.iterator(); iterator.hasNext();) {
			String p = (String) iterator.next();
			if (filePath.indexOf(p) != -1) {
				filePath = filePath.substring(p.length());
				if(filePath.startsWith(File.separator) && filePath.length()>=1){
					filePath=filePath.substring(1);
				}
				return filePath.endsWith(File.separator) ? filePath.substring(
						0, filePath.lastIndexOf(File.separator)) : filePath;
			}
		}
		return null;
	}

	public int write() throws Exception {
		log.info("CopycatFile.write() BEGIN");

		if (options.size() > 0) {
			byte[] bytes = (byte[]) options.get(INPUTSTREAM_OPTIONS);
			String fileName = (String) options.get(FILENAME_OPTIONS);
			String relativePath = (String) options.get(RELATIVE_PATH_OPTIONS);
			if (relativePath != null) {
				if (getTargetPath() == null) {
					throw new Exception("Destination dir missing!!");
				}
				String destinationDir = getTargetPath()
						.endsWith(File.separator) ? getTargetPath()
						+ relativePath : getTargetPath() + File.separator
						+ relativePath;
				System.out.println("Destination Dir: " + destinationDir);
				new File(destinationDir).mkdirs();
				if(!destinationDir.endsWith(File.separator)){
					destinationDir+=File.separator;
				}
				FileCopyUtils.copy(bytes, new FileOutputStream(new File(
						destinationDir + fileName)));
			}

		}
		if (options.size() == 0) {
			log.error("CopycatFile.write():: Options missing...");
		}
		return STREAM_WARNING;
	}

	public FilenamePattern getFileNamePattern() {
		return fileNamePattern;
	}

	public void setFileNamePattern(FilenamePattern fileNamePattern) {
		this.fileNamePattern = fileNamePattern;
	}

	public String getTempFileSuffix() {
		return tempFileSuffix;
	}

	public void setTempFileSuffix(String tempFileSuffix) {
		this.tempFileSuffix = tempFileSuffix;
	}

	public String[] getFileFilters() {
		return fileFilters;
	}

	public void setFileFilters(String[] fileFilters) {
		this.fileFilters = fileFilters;
	}

	public String getFileFilter() {
		return fileFilter;
	}

	public void setFileFilter(String fileFilter) {
		this.fileFilter = fileFilter;
	}

	public void afterPropertiesSet() throws Exception {
		if (getFileFilter() == null
				&& (getFileFilters() == null || getFileFilters().length == 0)) {
			throw new BeanInitializationException(
					"Setting a value for one of these properties: fileFilters or fileFilter");
		}
		if (getPath() == null && (getPaths() == null || getPaths().length == 0)) {
			throw new BeanInitializationException(
					"Setting a value for one of these properties: paths or path");
		}
		if (getPath() != null) {
			allPath.add(getPath());
		}
		if (getPaths() != null) {
			for (int i = 0; i < getPaths().length; i++) {
				String p = getPaths()[i];
				allPath.add(p);
			}
		}

		ff = new FileFilter() {

			public boolean accept(File theFile) {
				if (theFile.getName().equals(".")
						|| theFile.getName().equals("..")) {
					return false;
				}
				String[] filters = getFileFilters();
				String filter = getFileFilter();
				System.out.println(theFile.getAbsolutePath());
				AntPathMatcher pathMatcher = new AntPathMatcher();
				if (filters != null) {
					for (int i = 0; i < filters.length; i++) {
						String f = filters[i];
						if (theFile.isDirectory()) {
							String ap = theFile.getAbsolutePath();
							if (f.startsWith("/**")) {
								return true;
							}
							String[] splitted = StringUtils.split(f,
									File.separator);
							if (splitted != null) {
								for (int j = 0; j < splitted.length; j++) {
									String split = splitted[j];
									int idx = theFile.getAbsolutePath()
											.indexOf(split);
									if (idx == -1) {
										return false;
									}
									String pathSub = theFile.getAbsolutePath()
											.substring(idx, split.length());
									if (pathSub.startsWith("**")) {
										return true;
									}
								}
							}
						}
						boolean match = pathMatcher.match(f,
								theFile.getAbsolutePath());
						if (match) {
							return match;
						}
					}
				}
				if (filter != null) {
					return pathMatcher.match(filter, theFile.getAbsolutePath());
				}
				return false;
			}
		};

	}

	public String[] getPaths() {
		return paths;
	}

	public void setPaths(String[] paths) {
		this.paths = paths;
	}

	public String getPath() {
		return path;
	}

	public void setPath(String path) {
		this.path = path;
	}

	public void streamOptions(Map<Integer, Object> options) {
		this.options = options;
	}

	public String getTargetPath() {
		return targetPath;
	}

	public void setTargetPath(String targetPath) {
		this.targetPath = targetPath;
	}

}
